/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.servlet;

import com.blandware.atleap.common.Constants;
import com.blandware.atleap.common.util.ConvertUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.oro.text.regex.MalformedPatternException;
import org.apache.oro.text.regex.MatchResult;
import org.apache.oro.text.regex.Pattern;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;
import org.apache.struts.util.RequestUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;

/**
 * <p>Retrieves plain text or HTML content from session by attribute, specified as request parameter <code>var</code> and writes it into response</p>
 * <p>Content-type can be specified as request parameter <code>type</code>. Valid values for <code>type</code> are <code>text/html</code> (by default) and <code>text/plain</code></p>
 * <p>Note that this servlet will correctly work with two types of HTML content: well-formed HTML or XHTML (with HTML, HEAD and BODY tags) and
 * piece of text, which will be wrapped with &lt;body&gt;&lt;/body&gt; and &lt;html&gt;&lt;/html&gt; Also head section will be created with &lt;base /&gt;
 * and &lt;meta http=equiv="Content-Type" content="text/html; charset=utf-8"/&gt; presented. <code>Href</code> attribute will contain current scheme, host, port and context path.
 * <p><a href="IFrameContentLoaderServlet.java.html"><i>View Source</i></a></p>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.2 $ $Date: 2006/08/03 10:08:08 $
 * @web.servlet name="iframeContentLoader" load-on-startup="3"
 * @web.servlet-mapping url-pattern="/servlet/iframeContentLoader/*"
 */
public class IFrameContentLoaderServlet extends HttpServlet {


	/**
	 * Commons Logging instance.
	 */
	protected transient final Log log = LogFactory.getLog(IFrameContentLoaderServlet.class);

	/**
	 * Initializes the servlet.
	 */
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
	}

	/**
	 * Destroys the servlet.
	 */
	public void destroy() {

	}

	/**
	 * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
	 *
	 * @param request  servlet request
	 * @param response servlet response
	 */
	protected void processRequest(HttpServletRequest request, HttpServletResponse response)
	        throws ServletException, IOException {

		String contentType = request.getParameter("type");
		if ( !Constants.MIME_TYPE_PLAIN.equals(contentType) && !Constants.MIME_TYPE_HTML.equals(contentType) ) {
			contentType = Constants.MIME_TYPE_HTML;
		}

		String var = request.getParameter("var");
		if ( var != null && var.length() != 0 && request.getSession().getAttribute(var) != null ) {
			StringBuffer content = new StringBuffer((String) request.getSession().getAttribute(var));
			request.getSession().removeAttribute(var);

			if ( Constants.MIME_TYPE_HTML.equals(contentType) ) {

				Perl5Compiler compiler = new Perl5Compiler();
				Perl5Matcher matcher = new Perl5Matcher();
				Pattern pattern = null;

				// prepare tags to insert if it will be needed
				StringBuffer baseHref = RequestUtils.createServerUriStringBuffer(request.getScheme(), request.getServerName(), request.getServerPort(), request.getContextPath() + "/");
				StringBuffer base = new StringBuffer("<base href=\"").append(baseHref).append("\" />");
				StringBuffer metaCT = new StringBuffer("<meta http-equiv=\"Content-Type\" content=\"").append(contentType).append("; charset=").append(Constants.DEFAULT_ENCODING).append("\" />");

				int headOpenEndOffset = -1;
				String lowerCasedContent = content.toString().toLowerCase();

				// check HTML tags
				String htmlOpenRE = "<html[^<>]*>";
				pattern = compilePattern(compiler, htmlOpenRE);
				if ( matcher.contains(lowerCasedContent, pattern) ) {
					// HTML tag is present. We assume, that HEAD tags also present. Search for META and BASE
					String headOpenRE = "<head[^<>]*>";
					pattern = compilePattern(compiler, headOpenRE);

					if ( matcher.contains(lowerCasedContent, pattern) ) {
						// open HEAD tag exists
						MatchResult result = matcher.getMatch();
						headOpenEndOffset = result.endOffset(0);

						// seach for BASE
						String baseRE = "<base[^<>]+href=[^<>]+>";
						pattern = compilePattern(compiler, baseRE);

						if ( !matcher.contains(lowerCasedContent, pattern) ) {
							// create BASE
							content.insert(headOpenEndOffset, base);
						}

						// search for META with content-type
						String metaCTRE = "<meta[^<>]+http-equiv=(\"|\')?content-type(\\1)?[^<>]+>";
						pattern = compilePattern(compiler, metaCTRE);

						if ( !matcher.contains(lowerCasedContent, pattern) ) {
							content.insert(headOpenEndOffset, metaCT);
						}

					}
				} else {
					// HTML open tag does not present
					content = new StringBuffer("<html>").append("<head>").append(metaCT).append(base)
					        .append("</head>").append("<body>").append(content).append("</body>").append("</html>");
				}
			}
			byte[] contentBytes = ConvertUtil.convertToByteArray(content.toString());
			OutputStream out = response.getOutputStream();

			if ( contentType.indexOf("charset") == -1 ) {
				contentType += "; charset=" + Constants.DEFAULT_ENCODING;
			}

			response.setContentType(contentType);
			response.setContentLength(contentBytes.length);
			out.write(contentBytes);
			out.flush();
			out.close();
		}
	}

	/**
	 * Compiles given regular expression and wraps any exception occured in ServletException
	 *
	 * @param compiler Compiler to use for compiling pattern
	 * @param regex    Regular expression to compile
	 * @return Compiled pattern
	 * @throws ServletException if any error occurs during compilation, e.g. pattern is malformed
	 */
	protected Pattern compilePattern(Perl5Compiler compiler, String regex) throws ServletException {
		try {
			Pattern pattern = compiler.compile(regex);
			return pattern;
		} catch ( MalformedPatternException e ) {
			throw new ServletException(e);
		}
	}

	/**
	 * Handles the HTTP <code>GET</code> method.
	 *
	 * @param request  servlet request
	 * @param response servlet response
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
	        throws ServletException, IOException {
		processRequest(request, response);
	}

	/**
	 * Handles the HTTP <code>POST</code> method.
	 *
	 * @param request  servlet request
	 * @param response servlet response
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
	        throws ServletException, IOException {
		processRequest(request, response);
	}

	/**
	 * Returns a short description of the servlet.
	 */
	public String getServletInfo() {
		return "Servlet to load content for iframe";
	}

}